﻿package edu.ncut.vod.struts.action;

import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Arrays;
import java.util.Map;
import java.util.Collection;
import java.util.Set;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionForm;

import org.springframework.context.ApplicationContext;
import org.springframework.metadata.commons.CommonsAttributes;

import edu.ncut.vod.struts.form.AdminServiceForm;
import edu.ncut.vod.struts.action.base.BaseActionSupport;
import edu.ncut.vod.hibernate.model.TableList;
import edu.ncut.vod.hibernate.service.TableListService;

public class AdminServiceAction extends BaseActionSupport {

    /**
     * store every field in a row.It is used at sentence below
     */
	private Map<String,Object> fieldValue;

    public Map<String, Object> getFieldValue() {
		if(null == fieldValue){
			fieldValue = new HashMap<String,Object>();
		}
        return fieldValue;
    }

    public void setFieldValue(Map<String, Object> fieldValue) {
        this.fieldValue = fieldValue;
    }

    /**
     * specify the name of the head of each column
     */
    private Map<String,Object> fieldHead;

    public Map<String,Object> getFieldHead() {
        if(null == fieldHead){
			fieldHead = new HashMap<String,Object>();
		}
        return fieldHead;
    }

    public void setFieldHead(Map<String,Object> fieldHead) {
        this.fieldHead = fieldHead;
    }

    /**
     * perform the request of displaying the content of selected table in file 'home.jsp'
     */
	public ActionForward execute(ActionMapping mapping ,ActionForm form, HttpServletRequest request,HttpServletResponse response){
		debug("method execute in object AdminServiceAction is invoked");

		AdminServiceForm adminServiceForm = (AdminServiceForm)form;

		String tableSelection = adminServiceForm.getTableSelection();
		debug(tableSelection);

		/*when first enter file home.jsp,perform this method to fill dropdownlist in
		  file 'home.jsp'.Otherwise,show selected table in it.*/
		if(null == tableSelection){
			return performFillDropDownList(mapping,adminServiceForm);
		}else{
		    showSelectedTable(tableSelection,adminServiceForm);
		}

		return mapping.findForward("adminServiceJsp");
	}

	/**
	 * get service bean named 'tableListService'
	 * @return
	 */
	public TableListService getServiceBean(){
		debug("method getBean in object AdminServiceAction is invoked");

		//get Spring context
		ApplicationContext context = getWebApplicationContext();
		//return tableListService bean
		return (TableListService)context.getBean("tableListService");
	}

	/**
	 * when first enter file home.jsp,perform this method to fill dropdownlist in file 'home.jsp'
	 * @param mapping
	 * @param adminServiceForm
	 * @return
	 */
	public ActionForward performFillDropDownList(ActionMapping mapping,AdminServiceForm adminServiceForm){
		debug("method performFillDropDownList in object AdminServiceAction is invoked");

		//get tableListService bean
		TableListService tableListService = getServiceBean();

		List<TableList> tableList = tableListService.getAllTables();
		debug(tableList.toString());

		List<String> tableName = new ArrayList<String>();
		if(null != tableList){
			for(int i=0 ; i < tableList.size() ; i++){
				tableName.add(tableList.get(i).getTablename());
				debug(tableList.get(i).getTablename());
			}
		}
		adminServiceForm.setTableName(tableName);
		return mapping.findForward("adminService");
	}

	/**
	 * get character expression of getter method
	 * @param fieldName
	 * @return
	 */
	public String getMethodCharExpression(String fieldName){
	    debug("method getMethodCharExpression in object AdminServiceAction is invoked");

		String firstLetter=fieldName.substring(0,1).toUpperCase();
		return "get"+firstLetter+fieldName.substring(1);
	}

	/**
	 * determine if the content of the table is empty
	 * @param tableContent
	 * @param adminServiceForm
	 * @return
	 */
	public boolean isEmpty(List<?> tableContent, AdminServiceForm adminServiceForm){
		debug("method isEmpty in object AdminServiceAction is invoked");

		if(tableContent.isEmpty()){
			adminServiceForm.setContentEmpty(true);//display message about nothing to show
			adminServiceForm.setShowTable(false);//set the content of selected table invisible
			return true;
		}else{
			adminServiceForm.setContentEmpty(false);//hide message about nothing to show
			return false;
		}
	}

	/**
	 * insert table head into the first row of object 'showTableContent'
	 * @param it
	 * @param adminServiceForm
	 */
	public void insertTableHead(Iterator<?> it ,AdminServiceForm adminServiceForm){
		debug("method insertTableHead in object AdminServiceAction is invoked");

		if(it.hasNext()){

			//define object 'nextObject' as first parameter in method 'invoke'
			Object nextObject=it.next();

			Class<? extends Object> className=nextObject.getClass();

			//get all fileds of object
			Field[] fields = className.getDeclaredFields();
			//make field 'fieldHead' empty
			getFieldHead().clear();
			for(int i=0; i<fields.length; i++){
				try{
					String fieldName=fields[i].getName();
					if("serialVersionUID".equals(fieldName)){//do not get this field
						continue;
					}else{//add all fields except field 'serialVersionUID'
						getFieldHead().put(fieldName,(Object)fieldName);
					}
				}catch(IllegalArgumentException e){
					e.printStackTrace();
				}catch (SecurityException e) {
					e.printStackTrace();
                }
			}
		}
		adminServiceForm.getShownTableContent().add(0,(HashMap<String, Object>) fieldHead);
	}

	public void insertTableContent(Iterator<?> it ,AdminServiceForm adminServiceForm){
		debug("method insertTableContent in object AdminServiceAction is invoked");

		//index to specify what the index of the inserted row is.It is used at sentence below
		int k=1;
		//define object 'nextObject' as first parameter in method 'invoke'
		Object nextObject=it.next();

		Class<? extends Object> className=nextObject.getClass();

		Map<String,Object> fields = (Map<String,Object>)adminServiceForm.getShownTableContent().get(0);
		debug(fields.toString());

		Set<String> keys = fields.keySet();
		Iterator<String> iterator = keys.iterator();
		while(iterator.hasNext()){
			try{

				debug((String)fields.get(iterator.next()));
				String fieldName=(String)fields.get(iterator.next());

				//get character expression of getter method
				String methodName = getMethodCharExpression(fieldName);

				//get all methods of object
				Method[] methods = className.getDeclaredMethods();

				for(int j=0; j<methods.length; j++){

					if(methods[j].getName().equals(methodName)){//judge if this method is needed

						/*call needed method.
						  pay your attention: the first parameter of method 'invoke' needs an
						  instance of object*/
						String value;//store the returned value from getter method

						if(null==methods[j].invoke(nextObject,null)){//this field in table does not have any data
							value="empty";
						}else{
							value=methods[j].invoke(nextObject,null).toString();

							//when the returned value of the method is an object of 'Collection' or 'Map',set variable as 'ignored'
							if(methods[j].invoke(nextObject,null) instanceof Collection || methods[j].invoke(nextObject,null) instanceof Map){
								value="ignored";
							}

							//every PO object has overload method 'toString' which connects each field with string '#'
							if(value.contains("#")){//just get the first element in array which is about id
								String[] strArrays = value.split("#");
								value = strArrays[0];
							}
						}
						debug(value);

						if(!"ignored".equals(value)){//when value of variable 'value' is not 'ignored',put coresponding object into object Map named 'fieldValue'
							getFieldValue().put(fieldName,(Object)value);
							//adminServiceForm.getFieldName().add(fieldName);
						}else if("empty".equals(value)){//when value of variable 'value' is 'empty',put string 'empty' into object Map named 'fieldValue'
							getFieldValue().put(fieldName,"empty");
							//adminServiceForm.getFieldName().add(fieldName);
						}
						//adminServiceForm.getFieldName().add(fieldName);
					}
				}

			}catch(IllegalAccessException e){
				e.printStackTrace();
			}catch(IllegalArgumentException e){
				e.printStackTrace();
			}catch(InvocationTargetException e){
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			}
			/*add every Map object into List object named 'shownTableContent' in ActionForm
			 'adminServiceForm' for displaying in file 'home.jsp'*/
			adminServiceForm.getShownTableContent().add(k++,(HashMap<String, Object>) fieldValue);
		}
	}

	/**
	 * using Reflect API to get all fields information of any table
	 * @param tableSelection
	 */
	public void showSelectedTable(String tableSelection ,AdminServiceForm adminServiceForm){
		debug("method showSelectedTable in object AdminServiceAction is invoked");

		//get tableListService bean
		TableListService tableListService = getServiceBean();

		//get the content of concrete table which includes row and column
		List<?> tableContent = tableListService.getTableInformation(tableSelection);

		//when the content of the table is empty
		if(isEmpty(tableContent,adminServiceForm)){
			return;
		}

		//every element of object 'it' indicates one row
		Iterator<?> it = tableContent.iterator();

		//refresh object 'shownTableContent' when selecting a new table
		adminServiceForm.getShownTableContent().clear();
		//refresh object 'fieldName' when selecting a new table
		adminServiceForm.getFieldName().clear();

		/*if(!getFieldValue().isEmpty()){
			fieldValue.clear();
		}*/

		//insert table head into the first row of object 'showTableContent'
		insertTableHead(it,adminServiceForm);
		//insert table content into the object 'showTableContent'
		insertTableContent(it,adminServiceForm);

		//adminServiceForm.setFieldName(fieldHead);//.add(fieldName);

		//adminServiceForm.getShownTableContent().add((HashMap<String, Object>) fieldValue);
		adminServiceForm.setShowTable(true);
		debug(adminServiceForm.toString());
	}
}
